#include "gtest/gtest.h"

#include "leveldb/db.h"

using namespace leveldb;


TEST(LeveldbTest, test){
    const std::string dbName = "hello.db";
    DB* db;
    Options options;
    options.create_if_missing = true;

    Status st = DB::Open(options, dbName, &db);
    ASSERT_TRUE(st.ok());

    st = db->Put(WriteOptions(), "hello", "world");
    ASSERT_TRUE(st.ok());

    delete db;

    st = DestroyDB(dbName, options);
}


#include "LogWriter.h"

#include "db/log_reader.h"
#include "leveldb/env.h"

class LogReport : public ::leveldb::log::Reader::Reporter {
public:
    void Corruption(size_t bytes, const leveldb::Status & s) override {
        std::cout << "drops bytes:" << bytes << "status: " << s.ToString() << std::endl;
    }
};

TEST(LogWriterReaderTest, test){
    const char * log_file_name = "leveldb-test-log-writer.log";
    const std::string content(32*1024-7, 1);

    {
        WritableFile * file = nullptr;
        Status s = Env::Default()->NewWritableFile(log_file_name, &file);
        ASSERT_TRUE(s.ok() && file != nullptr);

        leveldb::log::NewWriter log_writer(file); 

        for(int i = 0; i < 2; ++i){
            log_writer.AddRecord(content); //get offset 
            //std::cout << log_writer.BlockId() << std::endl;
        }
        delete file;
    }


    { 
        SequentialFile * file = nullptr;
        Status s = Env::Default()->NewSequentialFile(log_file_name, &file);
        ASSERT_TRUE(s.ok() && file != nullptr);

        LogReport reporter;
        log::Reader reader(file, &reporter, false, 0);

        std::string scratch;
        Slice record;
        ASSERT_TRUE(reader.ReadRecord(&record, &scratch));

        ASSERT_TRUE(record == content);

        delete file;
    }

    Env::Default()->RemoveFile(log_file_name);
}


class NewLogWriter_BlockIdTest : public testing::Test{
public:
    NewLogWriter_BlockIdTest()
    :content_(32 * 1024-7, 'C')
    {

    }
    void SetUp() override{
        Status s = Env::Default()->NewWritableFile(file_name_, &file_);
        ASSERT_TRUE(s.ok() && file_ != nullptr);

        writer_ =  new leveldb::log::NewWriter(file_);
        ASSERT_TRUE(writer_ != nullptr);
    }
    void TearDown() override{
        delete writer_;
        delete file_;
        Env::Default()->RemoveFile(file_name_);
    }

protected:
    leveldb::WritableFile * file_ = nullptr;
    leveldb::log::NewWriter * writer_ = nullptr;
    const char * file_name_ = "new-log-writer--blockid.log";
    std::string content_;
};

TEST_F(NewLogWriter_BlockIdTest, testFirst){
    ASSERT_EQ(0llu, writer_->BlockId());
    writer_->AddRecord(content_); // 32K - 7
    ASSERT_EQ(32*1024ull, writer_->Offset());
    ASSERT_EQ(1llu, writer_->BlockId());

    writer_->AddRecord(content_); // 32K - 7
    ASSERT_EQ(2*32*1024ull, writer_->Offset());
    ASSERT_EQ(2llu, writer_->BlockId());

    writer_->AddRecord(content_); // 32K - 7
    ASSERT_EQ(3*32*1024ull, writer_->Offset());
    ASSERT_EQ(3llu, writer_->BlockId());
}

